#include <memory>
#include <cassert>
#include <string>
#include <algorithm>

#include "SRObjModel.h"
#include "SRData.h"
#include "SRObjModelLoader.h"

using std::string;
using std::vector;
using matsu::vec3;
using matsu::vec2;
using matsu::vec4;
using std::auto_ptr;

namespace sora
{
#if _IPHONE_
#pragma mark obj model elem
#endif
	ObjNodeData::ObjNodeData()
	{
		reset();
	}

	ObjNodeData::ObjNodeData(const std::string &line)
	{
		static ObjNodeFSM fsm;	//어차피 상태 얻기용으로만 쓰이니까 1회 생성+공유로 문제 없다
		vector<string> tokens = ObjModelParser::parser().split(line);
		ObjNodeKeyword keyword = ObjModel::str2keyword(tokens[0]);
		ObjNodeState &state = fsm.state(keyword);
		state.parse(tokens, *this);
	}

	ObjNodeData::~ObjNodeData()
	{
	}

	void ObjNodeData::reset()
	{
		useTex = false;
		useNormal = false;

		keyword_ = ObjNodeKeywordNull;

		//value를 쓰는것의 대부분의 기본값이 0이므로 일단 0으로 설정하고 켜는 식으로 사용
		memset(value, 0, sizeof(float) * 4);
		valueCnt = 0;

		strList_.clear();
		intList_.clear();
	}

	void ObjNodeData::destroy(std::vector<ObjNodeData*> datalist)
	{
		vector<ObjNodeData*>::iterator it;
		for(it = datalist.begin() ; it != datalist.end() ; it++)
		{
			delete(*it);
		}
		datalist.clear();
	}

	ObjNodeKeyword ObjNodeData::keyword() const { return keyword_; }
	ObjNodeCategory ObjNodeData::category() const
	{
		return ObjModel::str2category(keyword());
	}
	const std::vector<std::string> &ObjNodeData::strList() const { return strList_; }
	const std::string &ObjNodeData::str(int index) const { return strList_[index]; }
	int ObjNodeData::strCnt() const { return strList_.size(); }

	const std::vector<int> &ObjNodeData::intList() const { return intList_; }
	int ObjNodeData::intValue(int index) const { return intList_[index]; }
	int ObjNodeData::intCnt() const { return intList_.size(); }

	int ObjNodeData::numVertex() const
	{
		assert(category() == ObjNodeCategoryElement);
		assert(intList_.size() % singleVertexNeedInt() == 0);
		return intList_.size() / singleVertexNeedInt();
	}
	int ObjNodeData::singleVertexNeedInt() const
	{
		assert(category() == ObjNodeCategoryElement);
		//1개의 vertex를 찍기위해서 필요한 점의 갯수
		int num = 1;
		if(useTex)		num++;
		if(useNormal)	num++;
		return num;
	}
	vector<int> ObjNodeData::vertexInfo(int index) const
	{
		assert(category() == ObjNodeCategoryElement);
		//vertex 1개를 찍는데 필요한 인덱스 정보
		vector<int> info;
		int start = index * singleVertexNeedInt();
		for(int i = 0 ; i < singleVertexNeedInt() ; i++)
		{
			info.push_back(intList_[start + i]);
		}
		return info;
	}
	int ObjNodeData::vertex(int index) const
	{
		return intList_[index * singleVertexNeedInt()];
	}
	int ObjNodeData::tex(int index) const
	{
		SR_ASSERT(useTex == true);
		if(useNormal == false)
			return intList_[index * singleVertexNeedInt() + 1];
		else
			return intList_[index * singleVertexNeedInt() + 1];	//vertex-tex-normal
	}
	int ObjNodeData::normal(int index) const
	{
		SR_ASSERT(useNormal == true);
		if(useTex == false)
			return intList_[index * singleVertexNeedInt() + 1];
		else
			return intList_[index * singleVertexNeedInt() + 2];	//vertex-tex-normal
	}

#if _IPHONE_
#pragma mark obj model
#endif
	ObjNodeKeyword ObjModel::str2keyword(const std::string &value)
	{
		const std::string &token = value;

		//주석 or 빈거이면
		if(value.length() == 0 || value[0] == '#')
			return ObjNodeKeywordNull;

		if(token == "v")
			return ObjNodeKeywordGeometricVertex;
		else if(token == "vt")
			return ObjNodeKeywordTextureVertex;
		else if(token == "vn")
			return ObjNodeKeywordVertexNormal;
		else if(token == "vp")
			return ObjNodeKeywordParameterSpaceVertex;

		//free-form curve-surface attribue

		//elements
		else if(token == "p")
			return ObjNodeKeywordPoint;
		else if(token == "l")
			return ObjNodeKeywordLine;
		else if(token == "f")
			return ObjNodeKeywordFace;
		else if(token == "curv")
			return ObjNodeKeywordCurve;
		else if(token == "curv2")
			return ObjNodeKeywordCurve2d;
		else if(token == "surf")
			return ObjNodeKeywordSurface;

		//free-form curve/surface body statement

		//connectivity between freeo-form surface

		//grouping
		else if(token == "g")
			return ObjNodeKeywordGroupName;

		//display/render attribute
		else if(token == "usemtl")
			return ObjNodeKeywordMaterialName;
		else if(token == "mtllib")
			return ObjNodeKeywordMaterialLibrary;
		else
			assert("not support");

		return ObjNodeKeywordNull;
	}
	ObjNodeCategory ObjModel::str2category(const std::string &value)
	{
		ObjNodeKeyword keyword = str2keyword(value);
		return str2category(keyword);
	}

	ObjNodeCategory ObjModel::str2category(ObjNodeKeyword keyword)
	{
		assert(keyword != ObjNodeKeywordNull);
		vector<ObjNodeKeyword> keywords;
		vector<ObjNodeKeyword>::iterator result;

		//vertex data keyword
		const ObjNodeKeyword vertexDataKeywords[] = {
			ObjNodeKeywordGeometricVertex,
			ObjNodeKeywordTextureVertex,
			ObjNodeKeywordVertexNormal,
			ObjNodeKeywordParameterSpaceVertex,
		};
		keywords = vector<ObjNodeKeyword>(vertexDataKeywords,
										   vertexDataKeywords + sizeof(vertexDataKeywords) / sizeof(ObjNodeKeyword));
		result = std::find(keywords.begin(), keywords.end(), keyword);
		if(result != keywords.end())
			return ObjNodeCategoryVertexData;

		//element keyword
		const ObjNodeKeyword elementKeywords[] = {
			ObjNodeKeywordPoint,
			ObjNodeKeywordLine,
			ObjNodeKeywordFace,
			ObjNodeKeywordCurve,
			ObjNodeKeywordCurve2d,
			ObjNodeKeywordSurface,
		};
		keywords = vector<ObjNodeKeyword>(elementKeywords,
										   elementKeywords + sizeof(elementKeywords) / sizeof(ObjNodeKeyword));
		result = std::find(keywords.begin(), keywords.end(), keyword);
		if(result != keywords.end())
			return ObjNodeCategoryElement;

		//grouping
		const ObjNodeKeyword groupingKeywords[] = {
			ObjNodeKeywordGroupName,
		};
		keywords = vector<ObjNodeKeyword>(groupingKeywords,
										   groupingKeywords + sizeof(groupingKeywords) / sizeof(ObjNodeKeyword));
		result = std::find(keywords.begin(), keywords.end(), keyword);
		if(result != keywords.end())
			return ObjNodeCategoryGrouping;

		//display/render attribute
		const ObjNodeKeyword displayRenerAttributeKeywords[] = {
			ObjNodeKeywordMaterialName,
			ObjNodeKeywordMaterialLibrary,
		};
		keywords = vector<ObjNodeKeyword>(displayRenerAttributeKeywords,
										   displayRenerAttributeKeywords + sizeof(displayRenerAttributeKeywords) / sizeof(ObjNodeKeyword));
		result = std::find(keywords.begin(), keywords.end(), keyword);
		if(result != keywords.end())
			return ObjNodeCategoryDisplayRenderAttribute;

		assert(!"TODO: implement keyword and group");
		return ObjNodeCategoryVertexData;
	}
}

